#include "rect.h"
#include <GL/glew.h>
#include <stdlib.h>
#include <stdio.h>

/***************************************************************/
/*                  Constructeurs                              */
/***************************************************************/

Rect::Rect() : Displayable(oRect), width(1), height(1)
{
    color=Vectorf(1.0, 0.0, 0.0);
    InitBuffers();
}

Rect::Rect(float size) : Displayable(oRect),width(size), height(size)
{
    color=Vectorf(1.0, 0.0, 0.0);
    InitBuffers();
}

Rect::Rect(float size, Vectorf position) : Displayable(oRect, position), width(size), height(size)
{
     color=Vectorf(1.0, 0.0, 0.0);
     InitBuffers();
}

Rect::Rect(float size, Vectorf aposition, Vectorf arotation) : Displayable(oRect,aposition,arotation), width(size), height(size)
{
    color=Vectorf(1.0, 0.0, 0.0);
    InitBuffers();
}

Rect::Rect(float aWidth, float aHeight) : Displayable(oRect), width(aWidth), height(aHeight)
{
    color=Vectorf(1.0, 0.0, 0.0);
    InitBuffers();
}

Rect::Rect(float aWidth, float aHeight, Vectorf aTranslation): Displayable(oRect, aTranslation), width(aWidth), height(aHeight)
{
    color=Vectorf(1.0, 0.0, 0.0);
    InitBuffers();
}

Rect::~Rect()
{

}

/***************************************************************/
/*                  Méthodes                                   */
/***************************************************************/

void Rect::FillBuffer()
{
    glBindBuffer(GL_ARRAY_BUFFER,vertexBuffer);

    /*!
     * 	Je définis les coordonnées des sommets de mon carré
     */

    float pos[12]={
                        0		,0		,0,
                        width	,0		,0,
                        width	,height	,0,
                        0		,height	,0
                    };

    /*!
     * 	J'utilise la méthode glBufferData afin de remplir mon espace mémoire
     * 	premier argument : le type de buffer, deuxieme, la taille du buffer, 3 eme : les données, 4 eme ; truc à la con, voir doc
     */

    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*12, pos, GL_STREAM_DRAW);
}

void Rect::SetWidth(float aWidth)
{
    width=aWidth;
    FillBuffer();
}

void Rect::SetHeight(float aHeight)
{
    height=aHeight;
    FillBuffer();
}

void Rect::SetDimension(float aWidth, float aHeight)
{
    height=aHeight;
    width=aWidth;
    FillBuffer();
}


void Rect::SetColor(float x, float y, float z)
{
    color=Vectorf(x,y,z);
    FillColorBuffer();
}

void Rect::Picking()
{
    glPushMatrix();	//	On utilise PushMatrix afin que toutes les opérations sur la matrice de visualisation restent locales

    glTranslatef(translation->x(),translation->y(),translation->z());

    glRotatef(rotation->x(),1,0,0);
    glRotatef(rotation->y(),0,1,0);
    glRotatef(rotation->z(),0,0,1);

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glVertexPointer(3, GL_FLOAT, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER,pickingBuffer);
    glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0);

    /* activation des tableaux de sommets */

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, 0);

    /* desactivation des tableaux de sommet */

    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    glFlush();
    glPopMatrix();
}

void Rect::SetPicking(unsigned char x, unsigned char y, unsigned char z)
{
    colorxp=x;
    coloryp=y;
    colorzp=z;
    glGenBuffers(1,&pickingBuffer);
    glBindBuffer(GL_ARRAY_BUFFER,pickingBuffer);

    unsigned char colors[12]=
    {
        x,y,z,
        x,y,z,
        x,y,z,
        x,y,z
    };

    glBufferData(GL_ARRAY_BUFFER,sizeof(unsigned char)*12,colors,GL_STREAM_DRAW);
}

void Rect::Display()
{
    glPushMatrix();	//	On utilise PushMatrix afin que toutes les opérations sur la matrice de visualisation restent locales

        glTranslatef(translation->x(),translation->y(),translation->z());

        glRotatef(rotation->x(),1,0,0);
        glRotatef(rotation->y(),0,1,0);
        glRotatef(rotation->z(),0,0,1);

        RenderBuffers();

    glPopMatrix();
}

void Rect::RenderBuffers()
{
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glVertexPointer(3, GL_FLOAT, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER,colorBuffer);
    glColorPointer(3, GL_FLOAT, 0, 0);

    /* activation des tableaux de sommets */

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, 0);

    /* desactivation des tableaux de sommet */

    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    glFlush();
}


void Rect::InitBuffers()
{

        /*!
         * 	Dans un premier temps, je fais appel à la méthode glGenBuffers qui permet de créer un espace mémoire dans la carte
         * graphique qui sera utilisé pour stocker des informations de différents types (coordonnées du sommet, couleurs, normales
         * etc)
         * 	Chaque espace mémoire ainsi alloué sera identifié par l'entier retourné par cette fonction
         */

        glGenBuffers(1,&vertexBuffer);

        /*!
         * 	On utilise glBindBuffer pour définir l'espace mémoire courant.
         * 	Toutes les opérations en rapport avec les buffers modifieront l'espace mémoire courant défini par cette méthode
         */

        glBindBuffer(GL_ARRAY_BUFFER,vertexBuffer);

        /*!
         * 	Je définis les coordonnées des sommets de mon carré
         */

        float pos[12]={
                            0		,0		,0,
                            width	,0		,0,
                            width	,height	,0,
                            0		,height	,0
                        };

        /*!
         * 	J'utilise la méthode glBufferData afin de remplir mon espace mémoire
         * 	premier argument : le type de buffer, deuxieme, la taille du buffer, 3 eme : les données, 4 eme ; truc à la con, voir doc
         */

        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*12, pos, GL_STREAM_DRAW);

        /*!
         * 	Même opération pour stocker les informations concernant la couleur des sommets
         */

        glGenBuffers(1, &colorBuffer);
        glBindBuffer(GL_ARRAY_BUFFER,colorBuffer);

        float colors[12]=
        {
            color.x(),color.y(),color.z(),
            color.x(),color.y(),color.z(),
            color.x(),color.y(),color.z(),
            color.x(),color.y(),color.z()
        };

        glBufferData(GL_ARRAY_BUFFER,sizeof(float)*12,colors,GL_STREAM_DRAW);


        /*!
         * 	Initialisation du buffer gérant les indices
         */

    glGenBuffers(1, &indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);

    /*!
     * 	Indice des éléments (couleurs, coordonnées spatiales, à afficher)
     */

    GLuint indexes[6]=
    {
                0,1,2,3,0,1
        };

    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLuint)*6,indexes,GL_STREAM_DRAW);

}

void Rect::FillColorBuffer()
{
    glBindBuffer(GL_ARRAY_BUFFER,colorBuffer);

    float colors[12]=
    {
        color.x(),color.y(),color.z(),
        color.x(),color.y(),color.z(),
        color.x(),color.y(),color.z(),
        color.x(),color.y(),color.z()
    };

    glBufferSubData(GL_ARRAY_BUFFER,0,sizeof(float)*12,colors);
}
